﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace STS_wpf_tester
{
    public class STSdbMTL_TestManager
    {
        STSdb.MTL.STSdbMTLengine _sts_engine = null;
        string _dbPath = String.Empty;

        public STSdbMTL_TestManager(STSdb.MTL.STSdbMTLengine sts_engine)
        {
            _sts_engine = sts_engine;
        }


        public STSdbMTL_TestManager(string dbPath)
        {
            _dbPath = dbPath;
            _sts_engine = new STSdb.MTL.STSdbMTLengine(_dbPath);
        }

        public STSdb.MTL.STSdbMTLengine Engine
        {
            get { return this._sts_engine; }
        }


        #region "Atom Insert Tests"

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="value"></param>
        /// <param name="insertAtOnceQunatity"></param>
        /// <param name="insertTimes"></param>
        /// <param name="identifierOfCaller"></param>
        public void InsertBatch_LongString(string tableName, string value,int insertAtOnceQunatity, int insertTimes,int identifierOfCaller,bool showInsertIterationResults, bool showCommitIterationResults, bool showTotalExecutionResults)
        {
            System.Diagnostics.Stopwatch sw = new Stopwatch();
            System.Diagnostics.Stopwatch sw1 = new Stopwatch();
            System.Diagnostics.Stopwatch sw2 = new Stopwatch();
                        
            long ll = 0;
            int qBulk = insertAtOnceQunatity;         //inserts at once
            int qOper = insertTimes;                  //how much times   qBulk * qOper = totalInsertedRecords

            //insert bulk
            Dictionary<long, string> div = new Dictionary<long, string>();
            
            sw.Start();

            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {

                for (int ii = 0; ii < qOper; ii++)
                {
                    div.Clear();

                    for (int i = 1; i <= qBulk; i++)
                    {
                        ll = ii * qBulk + i;
                        div.Add(ll,  ll.ToString() + "_" + value);
                    }

                    sw1.Reset();
                    sw1.Start();

                    tran.InsertBatch<long, string>(tableName, div);

                    sw1.Stop();

                    if(showInsertIterationResults)
                        Console.WriteLine("ID: {3}; inserted: {0}; {1} ms; {2} ticks;", (ii * qBulk + qBulk), sw1.ElapsedMilliseconds, sw1.ElapsedTicks, identifierOfCaller);
                }

                sw2.Start();
                tran.Commit();
                sw2.Stop();

                if(showCommitIterationResults)
                    Console.WriteLine("ID: {2}; COMMIT: {0} ms; {1} ticks;", sw2.ElapsedMilliseconds, sw2.ElapsedTicks, identifierOfCaller);
            }

            sw.Stop();

            if(showTotalExecutionResults)
                Console.WriteLine("ID: {2}; Inserted: {3}; TOTAL: {0} ms; {1} ticks; Now: {4}", sw.ElapsedMilliseconds, sw.ElapsedTicks, identifierOfCaller, (insertAtOnceQunatity * insertTimes),DateTime.Now.ToString("HH:mm:ss.ms"));
        }


        #endregion


        #region "Select"

        public void Select_READ_AllFromTable<T,T1>(string tableName)
        {
            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {
                foreach (var tr in tran.SelectForward<T, T1>(tableName, STSdb.MTL.eSelectMode.READ))
                {
                    Console.WriteLine("{0}: K: {1}; V: {2}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.ms"), tr.Key, tr.Value);
                }
            }
        }

        public void Select_READ_FromTable<T, T1>(string tableName,int quantity)
        {
            if (quantity <= 0)
                quantity = 20;

            if (quantity > 10000)
                quantity = 10000;

            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {
                foreach (var tr in tran.SelectForward<T, T1>(tableName, STSdb.MTL.eSelectMode.READ).Take(quantity))
                {
                    Console.WriteLine("{0}: K: {1}; V: {2}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.ms"), tr.Key, tr.Value);
                }
            }
        }

        #endregion


        #region "Inserts"

       
        /// <summary>
        /// Inserts into table with the Key: long (DateTime ticks, always growing up) and Value: byte[] 
        /// </summary>
        /// <param name="quantityToInsert">Default 20</param>
        /// <param name="tableName"></param>
        /// <param name="byteValueLength"></param>
        /// <param name="CommitInTheEnd"></param>
        public ulong Insert_Sequential_LongKey_RandomByteValues(int quantityToInsert, string tableName, int byteValueLength, bool CommitInTheEnd,bool showOutput)
        {
            ulong TotalCountOfRecordsInTheTable = 0;
            int qRow = 20;

            if (quantityToInsert != -1)
                qRow = quantityToInsert;

            long tm = 0;
            Random rnd = new Random();
            byte[] btValue = new byte[byteValueLength];

            System.Diagnostics.Stopwatch sw = new Stopwatch();
            sw.Start();
            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {
                //tran.RegisterWriteOrSynchroReadTables(tableName);

                //Console.WriteLine("Table")
                tm = DateTime.Now.Ticks;
                for (int i = 0; i < qRow; i++)
                {
                    tm+=115754;                    
                    //tm += rnd.Next() * rnd.Next() * rnd.Next();

                    rnd.NextBytes(btValue);

                    //var tr = tran.Select<long, byte[]>(tableName, STSdb.MTL.eSelectMode.READ_SYNCHRO, tm);

                    //if (tr.Exists)
                    //{
                    //}
                    
                    tran.Insert<long, byte[]>(tableName, tm, btValue);
                    
                    if(!CommitInTheEnd)
                        tran.Commit();
                }
                
                if(CommitInTheEnd)
                    tran.Commit();

                TotalCountOfRecordsInTheTable = tran.Count<long, byte[]>(tableName,true);
                //Console.WriteLine("WRITING" + tableName);
            }
            sw.Stop();

            long fl = (new FileInfo(_dbPath)).Length;

            if(showOutput)
                Console.WriteLine("//DONE Insert_Sequential_LongKey_RandomByteValues: FileLen: {3} bytes; Rows: {4}; {0} ms; {1} bytes/row; {2} ms/row", sw.ElapsedMilliseconds, fl / qRow, sw.ElapsedMilliseconds / qRow, fl, qRow);

            //e1.Dispose();
            //e1 = null;

            //DONE: FileLen: 3347010 bytes; Row: 2000000; 12656 ms; 1 bytes/row; 0 ms/row
            //DONE: FileLen: 4872882 bytes; Row: 2000000; 17712 ms; 2 bytes/row; 0 ms/row


            return TotalCountOfRecordsInTheTable;

        }

        /// <summary>
        /// Inserts random keys and value into table with the Key: byte[]  and Value: byte[] 
        /// </summary>
        /// <param name="quantityToInsert"></param>
        /// <param name="byteKeySize">Try not exceed 139 bytes, otherwise new KeyMap for the table must be created and handeled separately</param>
        /// <param name="byteValueSize"></param>
        /// <param name="tableName"></param>
        /// <param name="CommitInTheEnd"></param>
        public ulong Insert_Random_ByteKey_RandomByteValues(int quantityToInsert, int byteKeySize, int byteValueSize, string tableName, bool CommitInTheEnd, bool showOutput)
        {
            ulong TotalCountOfRecordsInTheTable = 0;
            Random rnd = new Random();
            int qRow = quantityToInsert;

            byte[] btValue = new byte[byteValueSize];
            byte[] btKey = new byte[byteKeySize];

            System.Diagnostics.Stopwatch sw = new Stopwatch();
            sw.Start();
            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {

                for (int i = 0; i < qRow; i++)
                {
                    rnd.NextBytes(btValue);
                    rnd.NextBytes(btKey);

                    tran.Insert<byte[], byte[]>(tableName, btKey, btValue);

                    if (!CommitInTheEnd)
                        tran.Commit();
                }

                if (CommitInTheEnd)
                    tran.Commit();

                TotalCountOfRecordsInTheTable = tran.Count<byte[], byte[]>(tableName);

            }
            sw.Stop();

            long fl = (new FileInfo(_dbPath)).Length;

            if (showOutput)
                Console.WriteLine("//DONE Insert_Random_ByteKey_RandomByteValues: FileLen: {3} bytes; Rows: {4}; {0} ms; {1} bytes/row; {2} ms/row", sw.ElapsedMilliseconds, fl / qRow, sw.ElapsedMilliseconds / qRow, fl, quantityToInsert);

            return TotalCountOfRecordsInTheTable;
        }

        #endregion

        public void StartActionInThread(Action a)
        {
            System.Threading.Thread tr1 = new System.Threading.Thread(() => { a(); });

            tr1.Start();

        }


        #region "CREATE"

        public void CREATE_EMPTY_TABLES(int quantityOfTables, string prefix, bool CommitInTheEnd)
        {
            System.Diagnostics.Stopwatch sw = new Stopwatch();
            sw.Start();
            using (STSdb.MTL.Transaction tran = _sts_engine.GetTransaction())
            {

                for (int i = 0; i < quantityOfTables; i++)
                {
                  
                    tran.Select<int, byte>(prefix + i.ToString(),STSdb.MTL.eSelectMode.READ,1);

                    //if (!CommitInTheEnd)
                    //    tran.Commit();
                }

                //if (CommitInTheEnd)
                //    tran.Commit();

            }
            sw.Stop();

            long fl = (new FileInfo(_dbPath)).Length;
            Console.WriteLine("//DONE CREATE_EMPTY_TABLES: FileLen: {0} bytes; Tables: {1}; {2} ms; {3} bytes/table; {4} ms/row", fl, quantityOfTables, sw.ElapsedMilliseconds, fl / quantityOfTables, sw.ElapsedMilliseconds / quantityOfTables);
        }
        #endregion


        public void SCHEMA_GetTableNames()
        {
            Console.WriteLine("-----------   TABLES NAMES -------------");
            foreach (var tn in _sts_engine.Scheme.GetAllTablesNames())
            {

                Console.WriteLine(tn);
                
            }

            Console.WriteLine("-----------   END OF TABLES NAMES -------------");
        }


    }
}
